﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using DrawTools.Editor;
using DrawTools.Editor.CanvasFigures;
using DrawTools.Editor.Tools;
using DrawTools.Tools;
using DrawTools.ViewModels;

namespace DrawTools.Forms
{
  public partial class CaptureForm : Form
  {
    private readonly Dictionary<ToolStripButton, Tool> _buttonsToToolsMap = new Dictionary<ToolStripButton, Tool>();

    private PropertiesForm _propertiesForm;
    private Tool _activeTool;

    public CaptureForm()
    {
      InitializeComponent();
    }

    public void ShowObjectSelectorContextMenu(Point point)
    {
      HidePropertiesDialog();

      IEnumerable<CanvasObject> objectsBehindCursor = canvasControl.GetObjectsAtPoint(point);

      contextMenu.Items.Clear();

      foreach (CanvasObject canvasObject in objectsBehindCursor)
      {
        var menuItem = new ToolStripMenuItem(canvasObject.GetType().Name);

        CanvasObject currentlyHighlightedObject = canvasObject;
        CanvasObject selectedObject = null;

        menuItem.Click += (sender, args) =>
        {
          canvasControl.GraphicsList.DeselectAll();
          currentlyHighlightedObject.IsSelected = true;
          selectedObject = currentlyHighlightedObject;
          canvasControl.Refresh();
        };

        menuItem.MouseEnter += (sender, args) =>
        {
          selectedObject = canvasControl.GraphicsList.SelectedObjects.FirstOrDefault();
          canvasControl.GraphicsList.DeselectAll();
          currentlyHighlightedObject.IsSelected = true;
          canvasControl.Refresh();
        };

        menuItem.MouseLeave += (sender, args) =>
        {
          canvasControl.GraphicsList.DeselectAll();
          if (selectedObject != null)
            selectedObject.IsSelected = true;
          canvasControl.Refresh();
        };

        contextMenu.Items.Add(menuItem);
      }

      contextMenu.Show(this, point);
    }

    private void SetActiveToolByButton(ToolStripButton btn)
    {
      if (btn == null)
        return;

      foreach (var button in _buttonsToToolsMap.Keys)
      {
        button.Checked = false;
      }

      btn.Checked = true;
      _activeTool = _buttonsToToolsMap[btn];
    }

    private void ShowPropertiesDialog()
    {
      var selectedObjects = canvasControl.GraphicsList.SelectedObjects.ToList();

      if (!selectedObjects.Any())
        return;

      _propertiesForm.SetViewModel(new PropertiesFormViewModel(selectedObjects));

      if (!_propertiesForm.Visible)
      {
        //TODO: location should be calculated according of all objects
        _propertiesForm.Location = GetPropertiesFormLocation(selectedObjects.First());
        _propertiesForm.Show(this);
        //this.Focus();
      }
    }

    private void HidePropertiesDialog()
    {
      _propertiesForm.Hide();
      _propertiesForm.SetViewModel(null);
    }

    private Point GetPropertiesFormLocation(CanvasObject canvasObject)
    {
      const int offset = 30;
      Rectangle rect = RectangleToScreen(canvasObject.GetBounds());

      if (_propertiesForm.Location != Point.Empty && !rect.IntersectsWith(_propertiesForm.Bounds))
        return _propertiesForm.Location;

      if (rect.Contains(_propertiesForm.Bounds))
        return _propertiesForm.Location;

      Rectangle screenBounds = Screen.FromPoint(rect.Location).WorkingArea;
      screenBounds.Inflate(-offset, -offset);
      screenBounds.Offset(offset / 2, offset / 2);

      var areas = new[]
      {
        new Rectangle(screenBounds.X, screenBounds.Y, _propertiesForm.Width, _propertiesForm.Height),
        new Rectangle(screenBounds.Width - _propertiesForm.Width, screenBounds.Y, _propertiesForm.Width, _propertiesForm.Height),
        new Rectangle(screenBounds.X, screenBounds.Height - _propertiesForm.Height, _propertiesForm.Width, _propertiesForm.Height),
        new Rectangle(screenBounds.Width - _propertiesForm.Width, screenBounds.Height - _propertiesForm.Height, _propertiesForm.Width, _propertiesForm.Height)
      };

      foreach (var area in areas)
      {
        if (!rect.IntersectsWith(area))
          return area.Location;
      }

      return new Point(rect.X + offset, rect.Y + offset);
    }

    #region Event Handlers

    private void OnFormLoad(object sender, EventArgs e)
    {
      EditorTheme theme = canvasControl.Theme;

      _buttonsToToolsMap.Add(tbPointer, new PointerTool());
      _buttonsToToolsMap.Add(tbRectangle, new RectangleTool(theme.RectangleSettings));
      _buttonsToToolsMap.Add(tbEllipse, new EllipseTool(theme.EllipseSettings));
      _buttonsToToolsMap.Add(tbArrow, new ArrowTool(theme.ArrowSettings));
      _buttonsToToolsMap.Add(tbPolygon, new PolylineTool());
      _buttonsToToolsMap.Add(tbText, new TextTool(theme.TextSettings));

      //sets only once in the beginning. we don't need crop tool afterwards
      _activeTool = new CropTool();

      _propertiesForm = new PropertiesForm(canvasControl, theme);
      _propertiesForm.Closing += OnPropertiesFormClosing;
    }

    private void OnToolbarButtonClick(object sender, EventArgs e)
    {
      SetActiveToolByButton(sender as ToolStripButton);
    }

    private void OnExitClick(object sender, EventArgs e)
    {
      Application.Exit();
    }

    private void OnCanvasMouseDown(object sender, MouseEventArgs e)
    {
      if (e.Button == MouseButtons.Left)
      {
        _activeTool.OnMouseDown(canvasControl, e);
      }
      else if (e.Button == MouseButtons.Right)
      {
        ShowObjectSelectorContextMenu(e.Location);
      }
    }

    private void OnCanvasMouseMove(object sender, MouseEventArgs e)
    {
      if (e.Button == MouseButtons.Left || e.Button == MouseButtons.None)
        _activeTool.OnMouseMove(canvasControl, e);
      else
        canvasControl.SetCursor(Cursors.Default);
    }

    private void OnCanvasMouseUp(object sender, MouseEventArgs e)
    {
      if (e.Button == MouseButtons.Left)
      {
        _activeTool.OnMouseUp(canvasControl, e);
        SetActiveToolByButton(tbPointer);
      }
    }

    private void OnPropertiesFormClosing(object sender, CancelEventArgs cancelEventArgs)
    {
      cancelEventArgs.Cancel = true;
      HidePropertiesDialog();
    }

    private void OnHidePropertiesDialog(object sender, EventArgs e)
    {
      HidePropertiesDialog();
    }

    private void OnShowPropertiesDialog(object sender, EventArgs e)
    {
      ShowPropertiesDialog();
    }

    #endregion
  }
}